/*
 * Copyright 2016-2019 yoara
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package yhao.infra.common.util;


import org.apache.commons.lang3.StringUtils;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

/**
 * Map工具
 *
 * @author yoara
 */
public class CommonMapUtil {
	/**
	 * 从map中获取double值，如果map空或键为空，则返回默认值0
	 * @param map
	 * @param key
	 * @return
	 */
	public static double getDouble(Map<String, Object> map, String key) {
		return getDouble(map, key, 0);
	}

	/**
	 * 从map中获取double值，如果map空或键为空，则返回给定默认值
	 * @param map
	 * @param key
	 * @return
	 */
	public static double getDouble(Map<String, Object> map, String key, double defaultValue) {
		if (null == key || null == map) return defaultValue;
		try {
			return Double.parseDouble(map.get(key).toString());
		} catch (Exception e) {
		}
		return defaultValue;
	}

	/**
	 * 从map中获取int值，如果map空或键为空，则返回默认值0
	 * @param map
	 * @param key
	 * @return
	 */
	public static int getInt(Map<String, Object> map, String key) {
		return getInt(map, key, 0);	
	}

	/**
	 * 从map中获取int值，如果map空或键为空，则返回默认给定值
	 * @param map
	 * @param key
	 * @return
	 */
	public static int getInt(Map<String, Object> map, String key, int defaultValue) {
		if (null == key || null == map) return defaultValue;
		try {
			String str = String.valueOf(map.get(key));
			if (StringUtils.isNotEmpty(str)) {
				str = str.replaceAll(",", "");
				return Integer.valueOf(str);
			}
		} catch (Exception e) {
		}
		return defaultValue;
	}

	/**
	 * 从map中获取boolean值，如果map空或键为空，则返回默认值false
	 * @param map
	 * @param key
	 * @return
	 */
	public static boolean getBoolean(Map<String, Object> map, String key) {
		return getBoolean(map, key, false);
	}

	/**
	 * 从map中获取boolean值，如果map空或键为空，则返回默认给定值
	 * @param map
	 * @param key
	 * @return
	 */
	public static boolean getBoolean(Map<String, Object> map, String key, boolean defaultValue) {
		if (null == key || null == map) return defaultValue;
		try {
			return Boolean.valueOf(String.valueOf(map.get(key)));
		} catch (Exception e) {
		}
		return defaultValue;
	}

	/**
	 * 从map中获取值，如果map空或键为空，则返回默认值null
	 * @param map
	 * @param key
	 * @return
	 */
	public static String getString(Map<String, Object> map, String key) {
		return getString(map, key, null);
	}

	/**
	 * 从map中获取值，如果map空或键为空，则返回默认给定值
	 * @param map
	 * @param key
	 * @return
	 */
	public static String getString(Map<String, Object> map, String key, String defaultValue) {
		if (null == key || null == map) return defaultValue;
		try {
			String val = String.valueOf(map.get(key));
			if (StringUtils.isNotEmpty(val)) {
				return val;
			}
		} catch (Exception e) {
		}
		return defaultValue;
	}

	/**
	 * 从map中获取BigDecimal值，如果map空或键为空，则返回默认值null
	 * @param map
	 * @param key
	 * @return
	 */
	public static BigDecimal getBigDecimal(Map<String, Object> map, String key) {
		return getBigDecimal(map, key, null);
	}

	/**
	 * 从map中获取BigDecimal值，如果map空或键为空，则返回默认给定值
	 * @param map
	 * @param key
	 * @return
	 */
	public static BigDecimal getBigDecimal(Map<String, Object> map, String key, BigDecimal defaultValue) {
		if (null == key || null == map) return defaultValue;
		try {
			BigDecimal val = (BigDecimal) map.get(key);
			if (null != val) {
				return val;
			}
		} catch (Exception e) {
		}
		return defaultValue;
	}

	/**
	 * 从map中获取Object值，如果map空或键为空，则返回默认值null
	 * @param map
	 * @param key
	 * @return
	 */
	public static Object getObject(Map<String, Object> map, String key) {
		return getObject(map, key, null);
	}

	/**
	 * 从map中获取object值，如果map空或键为空，则返回默认给定值
	 * @param map
	 * @param key
	 * @return
	 */
	public static Object getObject(Map<String, Object> map, Object key, Object defaultValue) {
		if (null == key || null == map) return defaultValue;
		Object val = map.get(key);
		if (null != val) {
			return val;
		}
		return defaultValue;
	}

	/**
	 * 从map中获取给定泛型值，如果map空或键为空，则返回默认值null
	 * @param map
	 * @param key
	 * @return
	 */
	public static <T> T get(Map<String, Object> map, String key) {
		try {
			return (T) getObject(map, key, null);
		} catch (Exception e) {
		}
		return null;
	}

	/**
	 * 为Map设值
	 *
	 * @param map Map实例
	 * @param filter 是否过滤空键或空值
	 * @param k 键
	 * @param v 值
	 * @return 设置后的Map
	 */
	public static final <K, V> Map<K, V> put(Map<K, V> map, boolean filter, K k, V v) {
		if (map != null) {
			if (filter && k != null && v != null) {
				map.put(k, v);
			}
		}
		return map;
	}

	/**
	 * 为Map设值(过滤空键或空值)
	 *
	 * @param map Map实例
	 * @param k 键
	 * @param v 值
	 * @return 设置后的Map
	 */
	public static final <K, V> Map<K, V> put(Map<K, V> map, K k, V v) {
		return put(map, true, k, v);
	}

	/**
	 * 创建HashMap实例
	 *
	 * @return HashMap实例
	 */
	public static final <K, V> Map<K, V> NEW() {
		return new HashMap<>();
	}

	/**
	 * 创建HashMap实例
	 *
	 * @param initialCapacity 初始化容量
	 * @return HashMap实例
	 */
	public static final <K, V> Map<K, V> NEW(int initialCapacity) {
		return new HashMap<>(initialCapacity);
	}

	/**
	 * 创建HashMap实例
	 *
	 * @param filter 是否过滤空键或空值
	 * @param k 键
	 * @param v 值
	 * @return HashMap实例
	 */
	public static final <K, V> Map<K, V> NEW(boolean filter, K k, V v) {
		Map<K, V> map = NEW();
		put(map, filter, k, v);
		return map;
	}

	/**
	 * 创建HashMap实例
	 *
	 * @param filter 是否过滤空键或空值
	 * @param k1 键1
	 * @param v1 值1
	 * @param k2 键2
	 * @param v2 值2
	 * @return HashMap实例
	 */
	public static final <K, V> Map<K, V> NEW(boolean filter, K k1, V v1, K k2, V v2) {
		Map<K, V> map = NEW();
		put(map, filter, k1, v1);
		put(map, filter, k2, v2);
		return map;
	}

	/**
	 * 创建HashMap实例
	 *
	 * @param filter 是否过滤空键或空值
	 * @param k1 键1
	 * @param v1 值1
	 * @param k2 键2
	 * @param v2 值2
	 * @param k3 键3
	 * @param v3 值3
	 * @return HashMap实例
	 */
	public static final <K, V> Map<K, V> NEW(boolean filter, K k1, V v1, K k2, V v2, K k3, V v3) {
		Map<K, V> map = NEW();
		put(map, filter, k1, v1);
		put(map, filter, k2, v2);
		put(map, filter, k3, v3);
		return map;
	}

	/**
	 * 创建HashMap实例(过滤空键或空值)
	 *
	 * @param k 键
	 * @param v 值
	 * @return HashMap实例
	 */
	public static final <K, V> Map<K, V> NEW(K k, V v) {
		return NEW(true, k, v);
	}

	/**
	 * 创建HashMap实例(过滤空键或空值)
	 *
	 * @param k1 键1
	 * @param v1 值1
	 * @param k2 键2
	 * @param v2 值2
	 * @return HashMap实例
	 */
	public static final <K, V> Map<K, V> NEW(K k1, V v1, K k2, V v2) {
		return NEW(true, k1, v1, k2, v2);
	}

	/**
	 * 创建HashMap实例(过滤空键或空值)
	 *
	 * @param k1 键1
	 * @param v1 值1
	 * @param k2 键2
	 * @param v2 值2
	 * @param k3 键3
	 * @param v3 值3
	 * @return HashMap实例
	 */
	public static final <K, V> Map<K, V> NEW(K k1, V v1, K k2, V v2, K k3, V v3) {
		return NEW(true, k1, v1, k2, v2, k3, v3);
	}

	/**
	 * 过滤Map中的空键或空值
	 *
	 * @param map Map实例
	 * @return 过滤后的Map实例
	 */
	public static final <K, V> Map<K, V> cleanNullOrEmpty(Map<K, V> map) {
		if (isNotEmpty(map)) {
			for (K key : map.keySet()) {
				if (key == null || map.get(key) == null) {
					map.remove(key);
				}
			}
		}
		return map;
	}

	/**
	 * 判断Map是否为空(null||empty)
	 *
	 * @param map
	 * @return boolean
	 */
	public static final boolean isEmpty(Map<?, ?> map) {
		return map == null || map.isEmpty();
	}

	/**
	 * 判断Map是否非空
	 *
	 * @param map
	 * @return boolean
	 */
	public static final boolean isNotEmpty(Map<?, ?> map) {
		return !isEmpty(map);
	}

	/**
	 * 获取Map的size
	 *
	 * @param map
	 * @return int 为null时返回0
	 */
	public static final int size(Map<?, ?> map) {
		int size = 0;

		if (isNotEmpty(map)) {
			size = map.size();
		}

		return size;
	}


	/**
	 * 将一个 JavaBean 对象转化为一个  Map
	 * @param bean 要转化的JavaBean 对象
	 * @return 转化出来的  Map 对象
	 * @throws IntrospectionException 如果分析类属性失败
	 * @throws IllegalAccessException 如果实例化 JavaBean 失败
	 * @throws InvocationTargetException 如果调用属性的 setter 方法失败
	 */
	public static Map beanToMap(Object bean)
			throws IntrospectionException, IllegalAccessException, InvocationTargetException {
		Class type = bean.getClass();
		Map returnMap = new HashMap();
		BeanInfo beanInfo = Introspector.getBeanInfo(type);

		PropertyDescriptor[] propertyDescriptors =  beanInfo.getPropertyDescriptors();
		for (int i = 0; i< propertyDescriptors.length; i++) {
			PropertyDescriptor descriptor = propertyDescriptors[i];
			String propertyName = descriptor.getName();
			if (!propertyName.equals("class")) {
				Method readMethod = descriptor.getReadMethod();
				Object result = readMethod.invoke(bean, new Object[0]);
				if (result != null) {
					returnMap.put(propertyName, result);
				} else {
					returnMap.put(propertyName, "");
				}
			}
		}
		return returnMap;
	}

	/**
	 * 将一个 Map 对象转化为一个 JavaBean
	 * @param type 要转化的类型
	 * @param map 包含属性值的 map
	 * @return 转化出来的 JavaBean 对象
	 */
	@SuppressWarnings("rawtypes")
	public static Object mapToBean(Class type, Map map)
			throws NoSuchMethodException, IllegalAccessException,
			InvocationTargetException, InstantiationException, IntrospectionException {
		BeanInfo beanInfo = Introspector.getBeanInfo(type); // 获取类属性
		Object obj = type.getDeclaredConstructor(new Class[]{}).newInstance();

		// 给 JavaBean 对象的属性赋值
		PropertyDescriptor[] propertyDescriptors =  beanInfo.getPropertyDescriptors();
		for (int i = 0; i< propertyDescriptors.length; i++) {
			PropertyDescriptor descriptor = propertyDescriptors[i];
			String propertyName = descriptor.getName();

			if (map.containsKey(propertyName)) {
				// 下面一句可以 try 起来，这样当一个属性赋值失败的时候就不会影响其他属性赋值。
				Object value = map.get(propertyName);

				Object[] args = new Object[1];
				args[0] = value;

				descriptor.getWriteMethod().invoke(obj, args);
			}
		}
		return obj;
	}
}
